package org.NooLab.glue.filter;


import java.util.ArrayList;
import java.util.Vector;

import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.GUID;
import org.NooLab.utilities.strings.StringsUtil;



import org.NooLab.glue.MessageBoard;
import org.NooLab.glue.components.*;
import org.NooLab.glue.msgframe.*;
import org.NooLab.glue.net.BoardAddress;
import org.NooLab.glue.net.BoardAddresses;
import org.NooLab.glue.net.ComBoxIntf;
 
import org.NooLab.glue.recdispatcher.ReceptorLinks;
import org.NooLab.glue.storage.MsgBoardDataStorage;
import org.NooLab.glue.subscription.Future;
import org.NooLab.glue.subscription.Futures;
import org.NooLab.glue.subscription.FuturesIntf;
import org.NooLab.glue.subscription.MessageMatcherProcess;
import org.NooLab.glue.transaction.*;
 


public class ReceptorMessageInterpreter extends MessageInterpreter{

	
	
	Notifications waitingNotifications ;
	ClientMatcherIntf climatch ;
	
	SubscriptionHandlerInterpreter subscriptionHandler ;
	
	// this one is hosted by MessageBoard, but mostly working in SourceMessageInterpreter
	ReceptorLinks receptorLinks;
 	
	
	
	public ReceptorMessageInterpreter( MessageBoard messageboard, 
			 						   // FilterPackages filters, 
			 						   PrintLog outprn){
		
		super( messageboard, null, outprn );
		
		waitingNotifications = messageBoard.getWaitingNotifications();
		climatch = clientMatcher ; // new ClientMatcher(out) ;
		
		
		subscriptionHandler = new SubscriptionHandlerInterpreter( messageboard, null, outprn);

	
	}

	

	public int interpretingSingleReceptorMessage( Message msg  ){
		
		String[] rawIndicators ;
		int result=-1, p1;
		XMessage xmsg ;
		
		boolean isTransaction;
		String rawXmlMsg , role;
		  
		
		receptorLinks = messageBoard.getReceptorLinks();

		rawXmlMsg = msg.rawXmlMsg ;
		                                if ( rawXmlMsg.contains("<state>") )
		                                {
											out.print(4, "MessageBoard: interpreting message from receptor...");
											out.print(4, rawXmlMsg);
		                                }
		xmsg = new XMessage(out) ;
		
		
		originatorID = xmsg.getInfoFromProlog( rawXmlMsg, "origin", "id", ".");
		rawXmlMsg = xmsg.removePrologFromXml(rawXmlMsg) ;
		 
		if (originatorID.length()==0){
			originatorID = xmsg.getSpecifiedInfo(rawXmlMsg, "//originator", "id") ;
		}
		 
		try{
			
if ((rawXmlMsg.contains("<transaction>")) && (rawXmlMsg.contains("<data> **"))){
	out.printErr(2, "\r\nmessage from RECEPTOR to board... \n"+rawXmlMsg);
}

			role = xmsg.getSpecifiedInfo(rawXmlMsg, "//messageboard", "role").toLowerCase();
			
			// we also could use global mirrors of tid and iid for even more details...
			// then stuffing the monitor after interpretation...
			
			
			if (msgMonitor==null){
				msgMonitor = mbf.getMsgBoardMonitor();
			}

			if (msgMonitor!=null){
				msgMonitor.addObservation( 1, rawXmlMsg.length() ) ;
			}

			p1 = rawXmlMsg.indexOf("<transaction>");
			isTransaction= p1>0;
			
			if (rawXmlMsg.length() > 40){

				// catching state messages, inclusive confirmation / denial  of acceptance, 
				rawIndicators = new String[]{"<transaction>","<return ","type=\"state\""} ;
				if (recVariant_ReturnedStates( xmsg, rawXmlMsg ,rawIndicators) ){ return 0; }

				
				rawIndicators = new String[]{"<transaction>","<request ","type=\"return\"","<data>"} ;
				if (recVariant_ReturnedResults( xmsg, rawXmlMsg ,rawIndicators) ){ return 0; }
			
// wrong positive ?				
				rawIndicators = new String[]{"<transaction>","<request ","type=\"task","instanceID","content=\"resource"} ;
				if (recVariant_createProvideTaskResource( xmsg, rawXmlMsg ,rawIndicators) ){ return 0; }

				
				rawIndicators = new String[]{"<request ","type=\"subscription\"" ,"<context"} ;
				if (recVariant_DataAcceptAndRelayToSubscriber( xmsg, rawXmlMsg ,rawIndicators) ){ return 0; }
				
				
				// confirmation upon received task ....
				// then setting
				rawIndicators = new String[]{"transactionID value=","<state>","<transaction>","<return type=\"state"};
				if (recVariant_handlingTaskStateMessages( xmsg, rawXmlMsg ,rawIndicators) ){ return 0; }
				
				rawIndicators = new String[]{"<transaction id=","<state ","<transaction>","<confirmation "};
				if (recVariant_handlingTaskDeliveryConfirmation( xmsg, rawXmlMsg ,rawIndicators) ){ return 0; }

			} // message long enough ?
			
			
			p1 = rawXmlMsg.indexOf("<subscription") ;
			if ((p1>0) && (isTransaction==false)){
			
				// primary handling of subscriptions is the same for sources and receptors...
				
				// hence we route it..
				
				result = subscriptionHandler.go(rawXmlMsg) ;
				if (result==0){ return result;}
			}
			
			
		}catch(Exception e){
			out.printErr(1, e.getMessage() );
			e.printStackTrace() ;
		}
		
		
		return result;	
	}
 

	/**
	 * ResultsDeliveryByReceptor
	 * results are not direct sent back to the SOURCE!
	 * instead, the results are saved to a file, then a notification about the availability
	 * of the results is sent to the respective SOURCE;
	 * The source then requests the results, and get it by the MessageBoard 
	 * 
	 * WHY ARE WE HERE IN RECEPTORS ???
	 * 
	 * @param xmsg
	 * @param rawXmlMsg
	 * @param rawIndicators
	 * @return
	 */
	private boolean recVariant_ReturnedResults( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ) {
		
		boolean result=false ;
		boolean acceptUnknownTransactions=false;
		
		String bufferfilename,role,secret="";
		long transactionID, iid, p ;
		int targetport=0, ix, sv, ec;
		Notification note;
		XRegistration xreg ;
		Transaction transaction=null, transact;
		
		
		
		result = rawIndicatorsCheck( rawXmlMsg, rawIndicators);

		if ( result ){
			result = false;

				this.transactions = messageBoard.getTransactions();
				  
				
				transact = xmsg.readReturnedResults( transactions , acceptUnknownTransactions, rawXmlMsg ) ;
				
				// <instance id="2601"
				
				iid = xmsg.getSpecifiedValue(rawXmlMsg, "instance","id");
				
				xreg = XRegistration.getItemByID( iid, messageBoard.getRegisteredInstances() ) ;

				
				if (transact==null){
					
					transactionID = xmsg.getSpecifiedValue(rawXmlMsg, "transactionID", "value");
					secret = "unknown";
					
					targetport = xreg.getClientListeningPort() ;
					
					sv = Transaction.TS_ERR_TRANSINVALID ;   // -3;
					returnConfirmationToResultSender( xmsg, iid, transactionID , secret, targetport,sv);
					  
					return true;
				}
				 // now, the transaction contains the result object (in its b64 encoded form)
				if (transact!=null){
					// this "transact"ion is NOT from within the stored collection !
					// we have to switch the context...
					p = transact.getTransactionID();
					ix = this.transactions.getIndexByTransactionID( p, 1) ;
					if (ix>=0){
					
						transaction = transactions.getItem(ix) ;
						// transaction.setState( Transaction.TS_RESULTS_BUFFERED ) ;
					
						if ((iid>0) && (transaction.getInstanceID()<=0)){
							transaction.setInstanceID(iid) ;
						}
					}
					
					
					// transactions are already saved, yet without the results !!
					// caring for persistence, hibernating the received message
					// this uses "" for creating the filename
					
					bufferfilename = msgBoardStorage.saveTransactionResultsObj( transaction,"BOARD" ); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
					
					// note that the transaction also contains the ID of the originating SOURCE instance !
										if (fileutil.fileexists(bufferfilename)){
											// D:/dev/java/data/test/mb/transferBuffer/board/~rn-790-bo.btmp 
											out.print(2, "result package returned by instance iid: "+iid+" has been saved to buffer file : "+ bufferfilename);
										}else{
											out.print(2, "saving result package returned by instance iid: "+iid+" failed, intended buffer file would have been : "+ bufferfilename);
										}
					// bufferfilename is here:
					// D:/dev/java/data/test/mb/transferBuffer/board/~rn-524-so.btmp
					
					if (bufferfilename.length()==0){
						return true; // also: no confirmation
					}
					
					sv = transaction.getTransactState();
					ec = transaction.getTransactErrorState() ;
					if ((ec==0) && (sv<Transaction.TS_RESULTS_BUFFERED)){
						transaction.setState( Transaction.TS_RESULTS_BUFFERED ) ;
					}
					transactions.save() ;

												out.printErr(3, "\n>>> returned transaction: index="+ix+", id="+ transaction.getTransactionID() +
																" ("+p+"), state = "+transaction.getTransactState()+" : transactions-object = "+transactions.toString());
					
					if (messageBoard.getProtocolID() < MessageBoardFactoryProperties.protocolTCP){						
						if (messageBoard.checkForNotifyHandlerRunning()==false){
                    		delay(2);	
                    	}
					}
					// elsewhere, this will be routed to the SOURCE = originator of this transaction...
                    
                    
					// now, depending on the configuration of the SOURCE for pull or push,
					// we either prepare the message or wait for the source to as k for the results
					// the primary push message to the source is NOT the data, but just a notification;
					// this will NOT be prepared & sent from here, instead, we put this transaction ID
					// into a collection, which the MessageBoard is working on
					// i.e. any starting of sending data is ONLY on request of the source;
					// the difference being just that the source is informed or not
					
					transactionID = transaction.getTransactionID() ;
					 
					// also delete files in dir : D:\dev\java\data\test\mb\transactData
					String str = bufferfilename;
					
 					
					String downstreamBufferfilename = msgBoardStorage.createTransactionDataObjTmpFilename(transactionID, 0 );
					if (msgBoardStorage.getKeepAllBuffers()==false){ msgBoardStorage.removeBufferFile(downstreamBufferfilename) ;}
					
					String filename = downstreamBufferfilename.replace("/~tdata_", "/~reqray_") ;
					if (msgBoardStorage.getKeepAllBuffers()==false){ msgBoardStorage.removeBufferFile(filename) ; }
					
					filename = filename.replace("/~tdata_", "/~srcreq_") ;
					if (msgBoardStorage.getKeepAllBuffers()==false){ msgBoardStorage.removeBufferFile(filename) ;}
					// instead of removing, perhaps better archiving ?
					
					
					// setGlobalNotificationID() ;
					// then using: 
					msgBoardStorage.saveMsgBoardData() ;
					
					
					// this notifications should be ONLY for delivery to SOURCEs
					note = waitingNotifications.createNote( transactionID, bufferfilename );
					
					note.setNotificationID( transactionID + waitingNotifications.size()) ;  
					note.setIssuerInstanceID( transaction.getInstanceID() ) ;
					
					role = transaction.getRoleOfInitializer();
					note.setRole(role) ;
					// inactivate this note first ...
					note.setNotificationProcessRunning(true) ;
					note.setLastEventTime( System.currentTimeMillis() - 8000) ;
					
					if (messageBoard.getProtocolID() < MessageBoardFactoryProperties.protocolTCP){
						// for direct protocols ??? , we will initiate the notification directly
						waitingNotifications.add(note) ;					
						msgBoardStorage.saveNotificationsDictionary( waitingNotifications ) ;
					}

					// this unlocks the notifications for being sent !
					note.setNotificationProcessRunning(false) ;
					
					// then, after saving has been done, activate it
					
					// update state of transaction
					
					// transaction.setTransactState( Transaction.TS_RESULTS_BUFFERED);
					// transactions.save() ;
					
					
					// the transaction lives as long in the collection as it is not closed 
					// == source confirmed that the results has been delivered
					
					// not here, but from separate thread... notifySourceAboutResults( transactionID );
					// thread needs an "urgency" variable for each transaction!
					
					// now, the NotificationHandler will work on the collection of "waitingNotifications"
					// it will take the collection, then sending one note after another
					
					// actually, the result sender should use a secrecy.. 
					 
					if (xreg!=null){
						  
												out.print(2, "\nResults received from receptor ("+iid+") for transaction id="+transactionID+"") ;
												out.print(3, "\nrecVariant_ReturnedResults(), create confirmation on message id="+transactionID) ;
												out.print(4,  "received result :\n"+rawXmlMsg+"\n---------------------------------------------\n\n");
						
						targetport = (int) xmsg.getSpecifiedValue(rawXmlMsg, "instance", "port") ;
						targetport = xreg.getClientListeningPort() ;
						secret = transaction.getClientSecret() ;

						
						returnConfirmationToResultSender( xmsg, iid, transactionID , secret, targetport,0);
						 
												out.print(3, "Results confirmed to receptor ("+iid+") for transaction id="+transactionID+"...") ;
						
						if (messageBoard.getProtocolID() >= MessageBoardFactoryProperties.protocolTCP){
							
							out.print(3, "Results for transaction (tid:"+transactionID+") now going to send it to source...") ;
							relayReturnedResultsToSource( xmsg, rawXmlMsg, note, transactionID );
							 
						}
						

					}else{
						out.print(2, "registration = null, hence no confirmation will be returned to instance id="+iid+"." );
					}
					
					result = true;
				}// transaction ok ?
			} // quick test on msg ok?
			 
		return result;
	}
	  
	/**
	 * 
	 * this method not only offers a direct relay (via notification - triggered requests) of a single note, but
	 * also alignment if required, incl. batch sending of results, that are then complete
	 * 
	 * @param xmsg
	 * @param rawXmlMsg
	 * @param note
	 * @param transactionID
	 * @return
	 */
	private int relayReturnedResultsToSource( XMessage xmsg, String rawXmlMsg, Notification note, long transactionID){
	
		int result=-1,ix,deliveryPort=0 ;
		String xmloutmsg ;
		long iid,tid, now;
		boolean tiab;
		Transaction transaction;
		XRegistration xreg;
		Notifications waitingNotifications;
		Notification noty;
		try{
			 
			
			
			 
			now=System.currentTimeMillis() ;
			
			transaction = transactions.getTransactionByID(transactionID) ;
			
			// now we have to check for the originator of the transaction: who was the source by iid?
			// -> it is part of the transaction
			iid = transaction.getInstanceID() ;
			
			xreg = XRegistration.getItemByID( iid, messageBoard.getRegisteredInstances() ) ;
			 
			if (xreg==null){
				return -3;
			}
			 
			// now we know that this source currently registered 
			// get the the port, and send it 
			deliveryPort = xreg.getClientListeningPort() ;
			 
				// send a notification to the client that there is a result waiting
				// using method deliverResultsNotification() of embedded object 
				// the SOURCE client then digest the notification in replyVariant_NotificationAboutResults()
				// and will request the results, upon which the MB will 
				// return them through sourceVariant_ResultsReturnDeliveryAsResource()
				//  
												out.print(2, "direct release of notification in ReceptorMessageInterpreter::relayReturnedResultsToSource()...") ;
												
				messageBoard.handlingNotification(now, note);
				 
		}catch(Exception e){
			
		}
		
			 
		
		
		out.print(4, "relayReturnedResultsToSource(), through port:"+deliveryPort);
		
	
		out.delay(10);
		
		
		return result;
	}
	

	private boolean recVariant_DataAcceptAndRelayToSubscriber( XMessage xmsg, String rawXmlMsg, String[] rawIndicators){
		boolean result=false;
		
		long iid, tid;
		
		// "<request ","type=\"subscription\"" ,"<context" 
		result = xmsg.rawIndicatorsCheck( rawXmlMsg, rawIndicators);

		if ( result ){
			iid = xmsg.getinstanceID(rawXmlMsg, "instance");
			 // we should use a Subscription handler, that is common to sources and receptors...
												out.print(2, "subscription response received from receptor  (iid:"+iid+")...");
		    tid = xmsg.getTransactionID(rawXmlMsg);
		    // note, it may or may not contain a transaction, data, name !
		    
		    // read context, 
		    
		    // read subscription info
		    // subscriptionHandler.  ...
		}
		
		return result;
	}
	
	
	private boolean recVariant_ReturnedStates( XMessage xmsg, String rawXmlMsg, String[] rawIndicators ) {
		boolean result=false ;
		
		long iid, transactionID;
		int statevalue = -1, vi;
		String str,description;
		XRegistration xreg ;
		
		result = rawIndicatorsCheck( rawXmlMsg, rawIndicators);
	
		if ( result ){
			result = false;
	
			
			// state message by the receptor
			
			transactionID = xmsg.getTransactionID(rawXmlMsg);
			str = xmsg.getSpecifiedInfo(rawXmlMsg, "//state/item", "value") ;
				  if (str.length()>0){
					  statevalue = Integer.parseInt(str) ;
				  }
			iid = xmsg.getSpecifiedValue(rawXmlMsg, "instance","id");// getinstanceID(rawXmlMsg, "instance");
			
			description = xmsg.getSpecifiedInfo(rawXmlMsg, "//state/item", "description") ;
			
			
			if (transactionID>0){
				
				if (description.contentEquals("instance")){
					
					if (statevalue==11){
						vi = Transaction.TS_TASK_DENIED ;
					 
						out.printErr(2, "State message received (tid:"+transactionID+") from RECEPTOR ("+iid+"), transaction DENIED!");
						out.printErr(2, "Transaction will be re-routed.\n");
						// TODO:  ... just this...
						xmsg = new XMessage(out)  ;
						// get data about the receptor, which denied accepting the task
						iid = -1;
						messageBoard.handlingReRoutingOfTransaction( transactionID, iid)  ;
						
					}else{
						str = "("+statevalue+")";
						if (statevalue==Transaction.TS_TASK_ACCEPTED){
							str = "(TS_TASK_ACCEPTED)" ;
						}
						out.print(5, "State message received (tid:"+transactionID+") from RECEPTOR ("+iid+"), state value = "+str);
						// setting receptor iid to busy
						xreg = XRegistration.getItemByID(iid, registeredInstances) ;
						xreg.setReceptorState( XRegistration.RST_IS_RUNNING) ;
						// this prevents that we assign a second task to it via ClientMatcher 
					}
					return true;
				}
				
				if (statevalue==XRegistration.RST_IS_RUNNING){ // 20
					
					return true;
				}
				if (statevalue==XRegistration.RST_IS_IN_PROCESS){ // 25
					// there should be a completion message...
					out.print(4, "state message received (tid:"+transactionID+") from RECEPTOR (state:"+str+"), "+
								 "now routing to SOURCE:\n"+""+description);
					// TODO: create a state message to the SOURCE of this transaction
					relayStateMessageToSource( xmsg, rawXmlMsg, transactionID, str, description);
					return true;
				}
				
				// this serves also as a confirmation for the MessageBoard, that the data have been delivered successfully to the RECEPTOR 
				
				// we have to update the registration item -> is busy
				// find registration object
				// update it
				// -> XRegistration.RST_IS_RUNNING
				updateRegistrationState( iid, transactionID, statevalue ); // e.g. XRegistration.RST_IS_RUNNING ) ;
				return true;
				
			} else{  
											out.print(4, "RECEPTOR (id:"+iid+") announced availability to MessageBoard");
				// update registration object with availability flag ( XRegistration.RST_IS_AVAIL )
				updateRegistrationState( iid, transactionID, XRegistration.RST_IS_AVAIL ); 
			}
		 
		
			
		}
		
		return result ;
	}



	private void returnConfirmationToResultSender( XMessage xmsg,  long iid, long tid, String secret, int port, int stateFlag){
		
		String xmlstr, str="" ;
		int sv;
		Transaction transaction;
		BasicMessageParams basics = new BasicMessageParams();
		XRegistration xreg;
		
		
		
		basics.transactionID = tid;
		basics.instanceID = iid;
		 
		basics.privateTransactionIdentifier = "";
		basics.roleOfSender = "BOARD" ;
		
		if (stateFlag==0){
			transaction = transactions.getTransactionByID(tid) ;
			sv = transaction.getTransactState() ;
		} else{
			sv = stateFlag;
		}
		str = Transaction.decodeTransactionCode(sv) ;
		
		xmlstr = xmsg.createConfirmationAboutDelivery( basics,sv, "delivery", str ) ;
		
												out.printErr(3, "ReceptorMessageInterpreter, returnConfirmationToResultSender() preparing confirmation response : board -> receptor...");
												out.printErr(4, xmlstr+"\n");
		// add originator
		if (originatorID.length()>0){
												
			xmlstr = xmsg.insertRawToXml( originatorID, "originator","id", xmlstr, "</messageboard>",1);
		}
		xmlstr = xmlstr.replace("<!-- -->","");
		
		// 
		receptorLinks.removeByTransactionID(tid);
		
		int ix = receptorLinks.getActivatedPorts().indexOf(port);
		if (ix>=0){
			receptorLinks.getActivatedPorts().remove(ix);
		}
		xreg = XRegistration.getItemByID( iid, messageBoard.getRegisteredInstances() ) ;
		xreg.setReceptorState( XRegistration.RST_IS_AVAIL ); 
		
		// in case of http: within session reply
		SenderWrap sender = new SenderWrap( comBox ) ;
		int r=sender.sendingMessage( xmlstr, port) ;
		sender=null;
	}



	private void relayStateMessageToSource( XMessage xmsg, String rawXmlMsg, long transactionID, String stateValue, String stateInfo){
	/*
		 <messageboard name="spela" role="RECEPTOR">
			<transaction>
	   		<transactionID value="135"/>
	   		<instance id="2181"/>
	   		<return type="state"/>
	   		<state>
	      		<item description="task completion : 47.0%" value="25"/>
	   		</state>
			</transaction>
		</messageboard>
	*/
		String str, xmloutmsg,secrecystr, role;
		Transaction transaction;
		int deliveryPort;
		long instanceID;
		XRegistration xreg;
		out.print(5, "-------------\n"+
					 "state message for the Source \n"+
					 rawXmlMsg);
		 
		transaction = messageBoard.getTransactions().getTransactionByID(transactionID) ;
		
		if (transaction==null){
			// TODO message
			return;
		}
		secrecystr = transaction.getClientSecret() ;
		
		xmloutmsg = xmsg.createStateUpdateNotificationForSource( transactionID, secrecystr, -1 ,stateInfo, "RECEPTOR") ;
	
		
		deliveryPort =0; // -> to source , which opened this transaction
		
		instanceID = transaction.getInstanceID() ;
		
		xreg = XRegistration.getItemByID( instanceID, messageBoard.getRegisteredInstances() ) ;
		
		// meanwhile, the source might have disappeared...
		if ((xreg!=null) ){
			deliveryPort = xreg.getClientListeningPort();
		 
	
												out.print(4, "-------------\n"+
															 "prepared to send out: state message for the Source on port "+deliveryPort+" \r\n."+ xmloutmsg);
	        // || (asyncProtocol)
			if (originatorID.length()>0){
													
				xmloutmsg = xmsg.insertRawToXml( originatorID, "originator","id", xmloutmsg, "</messageboard>",1);
			}

			if (messageBoard.getProtocolID() >= MessageBoardFactoryProperties.protocolTCP){
												out.print(3, "informing source about states in relayStateMessageToSource(), through port:"+deliveryPort);
			    SenderWrap sender = new SenderWrap(messageBoard);
			    int r=sender.sendingMessage(xmloutmsg, deliveryPort);
			    out.delay(10);
			    sender = null;
			}else{
				// this still does not arrive at the SOURCE...
				
												out.print(3, "informing source about states in relayStateMessageToSource(), through port:"+deliveryPort);
				// for http transports we put it to the notifications 
				// TODO: upon contact by the SOURCE client, all state messages will be bundled into a single one
				Notification note = new Notification(); 
				note.setNotificationID( transaction.getTransactionID() );
				note.setIssuerInstanceID( transaction.getInstanceID() ) ;
				note.setRole("BOARD");
				note.setSystemMsg("notification");
				// not ready yet....
				// messageBoard.getWaitingNotifications().add(note);
			}
			
		}else{
			// no action to be taken for state notifications...
		}
	}



	/** 
	 * 
	 * in case of http-rest, Receptors periodically check for available tasks;
	 * such requests are routed to this method.
	 *  
	 * Receptors are competing with remote MessageBoards, which also send such requests.
	 * requests from MessageBoards have a lower priority, which is realized by the rule 
	 *    that they have to ask 2 times before they get a transaction
	 *    
	 *       TODO: we need a flag in the transaction indicating that treatment has been offered !
	 *       
	 * 
	 * 
	 * called by "recVariant_createProvideTaskResource()"
	 * @param exclIdsStr  list of values like 28;243;427;
	 * @param contentType the content type of the RECEPTOR-participant that has been declared in the request
	 * @return
	 */
	private int getNextSuitableTransactionIndex( String rawXmlMsg, String exclIdsStr , String contentType ){
		int result=-1;
		
		// matching is expensive and requires storing of states, so we have to avoid overlapping requests
		result = (new SelectingTransactionUponRequest( this, rawXmlMsg, exclIdsStr, contentType )).perform().getState();
		
		return result;
	}
	
	
	class SelectingTransactionUponRequest implements Runnable{

		ReceptorMessageInterpreter parent; 
		XMessage xmsg ;
		FuturesIntf futures;
		XRegistration xreg;
		
		String rawXmlMsg;
		String exclIdsStr ;
		String contentType;
		int index = -1;
		
		boolean isRunning=false;
		Thread strThrd;
		
		
		public SelectingTransactionUponRequest( ReceptorMessageInterpreter recmesgi, String rawXmlMsg, String exclIdsstr , String contenttype   ){
		
			parent = recmesgi;
			exclIdsStr  = exclIdsstr ;
			contentType = contenttype ;
			this.rawXmlMsg = rawXmlMsg;
			
			xmsg = new XMessage(out) ;
			
			strThrd = new Thread(this, "SelectingTransactionUponRequest") ;
 		}
		
		@Override
		public void run() {
			 isRunning=true;
			
			 index = performMatchingForTransactionIndex();
			 
			 isRunning=false;
		}

		public int getState() {
			 
			while (isRunning){
				delay(1);
			}
			return index;
		}

		public SelectingTransactionUponRequest perform() {
			 
			strThrd.start();
			out.delay(5);
			
			return this;
		}
	
		
	 
		private int performMatchingForTransactionIndex(  ){ 
			int index = -1;
			int ix=-1, z=0, esv,sv,fun=0;
			String ctype, secrecy, stateInfo , xmloutmsg;
			boolean found=false, hb, tidConfirmed;
			long ctid=0, instanceID=0;
			Vector<Future> futureColl;
			ArrayList<String> xidstr = new ArrayList<String>();
			ArrayList<Long> exludedIDs;
			Transaction transaction ;
			MessageMatcherProcess mmp;
			
			if (transactions==null){
				
				return -3;
			}
			
			instanceID = xmsg.getinstanceID(rawXmlMsg, "instanceID");
			xreg = XRegistration.getItemByID( instanceID, registeredInstances);
			
			
		    secrecy = xmsg.getSpecifiedInfo(rawXmlMsg, "//transaction/rquest", "secret"); 
		      
			// did this instance deliver a subscription?
			if (xreg==null){
				return -5;
			}    
			futures = messageBoard.getMsgBoardShares().getMsgBoardFactory().getFutures();
			futureColl = futures.getFuturesById( instanceID ) ;
			fun = futureColl.size();
			if (exclIdsStr.length()>0){
				xidstr = strgutil.changeArrayStyle( exclIdsStr.split(";") ) ; // strgutil.splitString(exclIdsStr, 1, 1)
			}
 		
			exludedIDs = strgutil.changeArrayType( xidstr ,0L, true); 
		
			// transactions.cleanFromInconsistentItems();
			int tz = transactions.size();
			while ((found==false)  && (z<tz+1)){
			
				//                                             note that the order within the array is important !
				ix = transactions.getIndexByStates( (new int[]{	Transaction.TS_TASK_CONFIRMED,Transaction.TS_TASK_DENIED,
																// Transaction.TS_TASK_MATCHING,
																Transaction.TS_TASK_ACCEPTED, Transaction.TS_ID_ASSIGNED}), exludedIDs, ix,1) ;	
					          // this excludes those with an error state
			        
				
			    if (ix>=0){
				
			    	transaction = transactions.getItem(ix) ;
			    	
			    	
			    	esv = transaction.getTransactErrorState();     
			    	if (esv!=0){
			    		continue;
			    	}
			    	
			    	sv = transaction.getTransactState() ;
			    	
			    		ctype = transaction.getContentType() ;
			    		// we need a specialized matching function for the content type, since we have complicated compound strings
			    		if (ctype.length()==0){ /* transactions ALWAYS need a contentType, use "*" for any */ }
			    		found = climatch.contentTypesMatching( contentType, ctype) ;
			    		
			    		if (found) {
			    			out.print(2,"performMatchingForTransactionIndex(): transaction (tid="+transaction.getTransactionID()+") currently tested for relay to Receptor requires subscription-context match ");

			    			if (transaction.getRelayByContext()>=1){
			    				
			    				if (fun>0){
			    					// check subscription of transaction ix with the context info of the transaction
			    					// the MessageBoard knows about a list of all issued contexts: "ActiveContextHeaderDescriptions{}"
			    					// "issued contexts" are NOT futures/subscriptions, they are part of the task message
			    				
			    					tidConfirmed = false;
			    				
			    					mmp = messageBoard.getMsgmatcherProcess();
			    					// providing the message via setMessageContent() is only for SOURCES !
			    					 
			    					// also : additional / different arguments...

			    					// checking whether this instance and the selected buffered transaction match
			    					// ctid will be >0 (and should match the transaction-id) 
			    					ctid = mmp.performMatchingForInstance( instanceID, transaction, "SOURCE",contentType);
			    				
			    					tidConfirmed = ctid>0;

			    					// return a valid index only if there is a match
			    					if (tidConfirmed){
			    						index = ix;
			    						break;
			    					}else{
				    					// else, continue, but exclude this tid from search
				    					exludedIDs.add( transaction.getInstanceID()) ;
			    					}
			    					
			    				} // fun>0 -> are there any subscriptions for this iid ?
			    				else{
			    					out.print(2,"   ... but instance did not deliver any subscription, thus match=FALSE...");
			    					// neither break, not select -> check the next transaction
			    					
			    					// only for fully wired transport protocols
			    					if (messageBoard.getProtocolID() >= MessageBoardFactoryProperties.protocolTCP){
			    						secrecy = transaction.getClientSecret();
			    						stateInfo = ""+ Transaction.TS_ERR_NOCONTEXT;  
			    						xmloutmsg = xmsg.createStateUpdateNotificationForSource(transaction.getTransactionID(), secrecy, -1, stateInfo, "RECEPTOR");

			    						SenderWrap sender = new SenderWrap( messageBoard);
										sender.sendingMessage(xmloutmsg, xreg);
			    					}
			    					
			    					found=false;
			    					exludedIDs.add( transaction.getTransactionID()) ;
			    				}
			    			}
			    			else{ // ok we found one
			    				index = ix;
			    				found=true;
			    				break ;
			    			}
			    		}
			    	
				
			    }else{
			    	break ;
			    }
			    z++;
			}
		
			if (index<0){
			
			}
			
			transaction=null;
			return index;
		} // perform()
		
	} // inner class
	/**
	 * this method is used only for asynchronous transport configurations, i.e. we are a true server here;<br/>
	 * the method creates and returns (=sending back) a key-value pair, where the <br/>
	 * key is an adhoc UUID and the value is a filename <br/>
	 * The key is returned WITHIN the httpc-session to the caller <br/> 
	 * 
	 * <br/>
	 * @param xmsg
	 * @param rawXmlMsg
	 * @param rawIndicators
	 * @return
	 */
	@SuppressWarnings("unused")
	private boolean recVariant_createProvideTaskResource(  XMessage xmsg, String rawXmlMsg, String[] rawIndicators) throws Exception{
		
		/* this has been produced by a RECEPTOR through "XMessage.createRequestForTaskResource(instanceID, role)" :
		       <request content="resource" type="task"/>
  				 <instanceID value="2538"/>
  				 <role value="RECEPTOR"/>
  				 
  		   now we are creating the direct response
		 */
		
		String xmlstr,role, guidStr,filename,xmloutmsg="",partsContentType="",excludedIdListStr="" ;
		 
		int deliveryPort, activeRelay=0 ,ix,sv ;
		long instanceID,  transactionID;
		boolean result;
		
		Object obj;
		XRegistration xreg ;
		//Transactions transactions; 
		Transaction transaction; 
		SenderWrap sender =null;
		
		
		result = rawIndicatorsCheck( rawXmlMsg, rawIndicators);

		if ( result ){
			// result = false;
												
			
			instanceID  = xmsg.getinstanceID(rawXmlMsg, "instanceID") ;
			role = xmsg.getSpecifiedInfo(rawXmlMsg, "role", "value") ;
		
			xreg = XRegistration.getItemByID( instanceID, messageBoard.getRegisteredInstances() ) ;
			
			if (xreg==null){
				
				sv = XRegistration.RST_IS_NOREGIST ;
												out.printErr(2, "registration for participant id="+instanceID+" is invalid upon re-connect.");
				
				returnAdviceForNewRegistration( xmsg, null, instanceID, sv);
				
				return result;				
			}
			
			if ( registrationOfInstance( instanceID) != 0){
				// TODO: return reply that the client should first register
				sv = XRegistration.RST_IS_TIMEDOUT ;
												out.printErr(2, "registration for participant id="+instanceID+" is invalid due to time out.");
				returnAdviceForNewRegistration( xmsg, xreg, instanceID, sv);
				return result;
			}
			
												// outx out.print(4, "checking for waiting transactions...");
												
			partsContentType = xmsg.getSpecifiedInfo(rawXmlMsg, "content", "type") ;
			
			if (partsContentType.length()==0){
				sv = XRegistration.RST_CTYPEUNDEF ;
				out.printErr(3, "content type not defined in request.");
				return result;
			}
			
			// RST_CTYPENOMATCH

			
			excludedIdListStr = xmsg.getSpecifiedInfo(rawXmlMsg, "excluded", "values") ;
			// this may contain a list of values like 28;243;427;
			
			guidStr = GUID.randomvalue() ;
			
			// we have to select an available = waiting transaction
			
			if (xreg!=null){
				deliveryPort = xreg.getClientListeningPort();
			}else{
				deliveryPort = 0;
			}
			
			
			transactions = messageBoard.getTransactions();
			 
			
			// we select the next transaction which is waiting,
			// yet we have to apply extended filters like content type, exclusions as conveyed by the receptor itself
			// of additional task states like "denied" ...
			// this would be a simple query
			// ix = transactions.getIndexByState( Transaction.TS_TASK_CONFIRMED, 1) ; // 1 = mode => exactly  
			
			// this also matches the content type !! in clientmatcher
			ix = getNextSuitableTransactionIndex( rawXmlMsg,excludedIdListStr , partsContentType);
		
			
			if (ix<0){
				// out x  out.print(4, "no waiting transactions found upon request as issued by instance id="+instanceID+".");
				
				xmlstr = xmsg.createNoResourceMsgForReceptor(instanceID, role,"");
				
				xmlstr = xmsg.insertRawToXml( originatorID, "originator","id", xmlstr, "</messageboard>",1);
				
				try{
					sender = new SenderWrap(messageBoard,"recmi");
					int r=sender.sendingMessage(xmlstr, deliveryPort);

				}catch(Exception e){
					e.printStackTrace();
				}finally{
					sender=null;
				}
				
				return result;
			}
												
			transaction = transactions.getItem(ix) ;
			
			if (transaction==null){
				return result;
			}
			
			transactionID = transaction.getTransactionID();
						
			// now find the filename , we do not have the instance ID of the SOURCE for this !! (transaction.getInstanceID())
		    // the xml prepared for the RECEPTOR does not contain the SOURCES instanceID !!
 
			filename = msgBoardStorage.createTransactionDataObjTmpFilename( transactionID, 0 );
			filename = filename.replace("/~tdata_", "/~reqray_") ;

			// the XML as relayed to the RECEPTOR in direct transport: => this will be delivered
			obj = msgBoardStorage.readTransactionDataObj( filename) ;
			
			if (obj instanceof Integer){
				int errcode = (Integer)obj ;
				// raise an error...
				transactions.removeItem(ix) ; // self-healing ...
				
				String str = "Buffered request file not found, or format error in \"recVariant_createProvideTaskResource()\", code="+errcode+"!\n\t"+filename+
			    	  		 "Transaction has been removed from list." ;
				// throw(new Exception(str));
				out.printErr(3, str) ;
				return true;
			}
			
												// recVariant_createProvideTaskResource
												out.print(2, "Transaction selected for RECEPTOR (id:"+instanceID+"): index="+ix+", transaction id="+ transactionID +", state = "+transaction.getTransactState()+ " ("+Transaction.decodeTransactionCode(transaction.getTransactState())+"->TS_TASK_DELIVERING)");
			// blocking it for other requests ...
			// transaction.setTransactState(100 + Transaction.TS_TASK_CONFIRMED ) ;
			transactions.annotateTransactionTaskForDelivery( ix, guidStr, instanceID );
			
												out.print(4,"creating response for task request, offering transaction id = "+transaction.getTransactionID()+" : transactions-object = "+transactions.toString());
												transaction.setTransactState(Transaction.TS_TASK_DELIVERING); // =14
												transaction.setTimeofDelivery(System.currentTimeMillis()) ;
												transactions.save() ;
												// upon confirmation -> TS_TASK_DIGESTING
			xmlstr = xmsg.createResourceMappingUponTaskRequest( transactionID, instanceID, role, guidStr);
												
			// this sequence re-constructs what would happen for direct transports in "sourceVariant_DataRelayToReceptor()" for
			// direct relay to the RECEPTOR 
			
		   
			xmloutmsg = (String)obj ;
			
			
			// adding the resource secret
		    xmlstr = xmsg.insertRawToXml( guidStr, "resource","value", xmloutmsg, "</messageboard>",1);
		    
			if (activeRelay==0){
				xmlstr = xmsg.insertRawToXml( originatorID, "originator","id", xmlstr, "</messageboard>",1);
			}
			
												out.print(4,""+xmlstr);
			
			// xreg = XRegistration.getItemByID( instanceID, messageBoard.getRegisteredInstances() ) ;
			
			// meanwhile, the source might have disappeared...
													 
				sender = new SenderWrap(messageBoard);
				int r=sender.sendingMessage(xmlstr, deliveryPort);
				sender = null;

		
		}
		return result;
	}
	
	
	private boolean recVariant_handlingTaskDeliveryConfirmation( XMessage xmsg, String rawXmlMsg, String[] rawIndicators) throws Exception{ 
		
 	  /* this is different from state maessages, as there the message is contained by full-level tags <state><item .... /></state>
	 	 <messageboard name="spela" role="RECEPTOR">
			<transaction>
				<transaction id="589"/>
				<confirmation secret="" type="TS_TASK_DIGESTING">
					<state text="TS_TASK_DIGESTING" value="16"/>
				</confirmation>
				<instance id="0"/>
			</transaction>
	     </messageboard> 
 	  */

		boolean result ;
		int stateValue;
		String str ;
		long tid;
		
		Transaction transaction;
		
		
		result = rawIndicatorsCheck( rawXmlMsg, rawIndicators);

		if ( result ){
												out.print(3, "State/Confirmation Message received from RECEPTOR : ") ;
												out.print(6,""+rawXmlMsg) ;
												
			stateValue = (int)((Integer)xmsg.getAvailableStateMsg( rawXmlMsg, "/confirmation/state", "state","value" ,0 )) ;
			
			tid = xmsg.getTransactionID(rawXmlMsg) ;
			transaction = transactions.getTransactionByID(tid) ;
			
			if (transaction != null){
				
				str = Transaction.decodeTransactionCode(stateValue) ;
				
				if (str.length()>0){
					// set new suggested value
					transaction.setTransactState(stateValue) ;
					transactions.save() ; // not necessarily consistent... ?
				} // state code is known == translation returns sth
			}// transaction  found
		} // indicators match ?
		
		return result ;
	}
	
	
	
	private boolean recVariant_handlingTaskStateMessages( XMessage xmsg, String rawXmlMsg, String[] rawIndicators) throws Exception{
		/*
		 *  <transaction>
	  			<transactionID value="524"/>
	  			<instance id="2578"/>
	  			<return type="state"/>
	  			<state>
	     			<item description="" value="20"/>
	  			</state>
				</transaction>
		 */

		boolean result ;
		int stateValue;
		long tid;
		
		Transaction transaction;
		
		
		result = rawIndicatorsCheck( rawXmlMsg, rawIndicators);

		if ( result ){
												out.print(3, "StateMessage received from RECEPTOR :");
												out.print(4, rawXmlMsg) ;
												
			stateValue = (int)((Integer)xmsg.getAvailableStateMsg( rawXmlMsg, "/state/item", "description","value"  ,0 )) ;
		
			tid = xmsg.getTransactionID(rawXmlMsg) ;
			transaction = transactions.getTransactionByID(tid) ;
			
			if (stateValue == Transaction.TS_TASK_ACCEPTED){
				if (transaction != null ){
					transaction.setState( Transaction.TS_TASK_DELIVERED ) ;
				}
			}
			// if description > "" relay to SOURCE
			/*  TODO
			 *  in case of httpc-rest
			 *  we should maintain a structure which holds for each of the of the transaction the list of notification
			 *  such that the SOURCE can retrieve them
			 *  
			 *  The SOURCE also should define the resolution of the completion notification !!
 			 */
		}
		
		return result;
	}
	
	/**
	 * 
	 * 
	 * @param xmsg
	 * @param instanceID
	 * @param stateValue
	 */
	private void returnAdviceForNewRegistration( XMessage xmsg, XRegistration xreg, long instanceID, int stateValue){
		
		String xmlstr , description ;
		int port =0;
		
		if (xreg!=null){
			port = xreg.getClientListeningPort() ;
		}
		
		// description = XRegistration.decodeRegStateCode(stateValue) ;
		xmlstr = xmsg.createNotificationAboutRegistration( instanceID, "BOARD",stateValue , "" ) ;
		
		// add originator
		if (originatorID.length()>0){
												out.printErr(3, "preparing response about registration, returnAdviceForNewRegistration() ...");
			xmlstr = xmsg.insertRawToXml( originatorID, "originator","id", xmlstr, "</messageboard>",1);
		}
		
		// in case of httpc-rest, this will always occur within-session as a direct reply ! 
												out.printErr(2, " "+xmlstr);
		SenderWrap sender = new SenderWrap( comBox ) ;
		int r=sender.sendingMessage( xmlstr,port ) ;
		sender=null;
	}
	
	private boolean rawIndicatorsCheck( String rawXmlMsg, String[] rawIndicators){
		boolean rB=false;
		int isum=0;
		
		int[] p = new int[rawIndicators.length] ;
		
		for (int i=0;i<rawIndicators.length;i++){
			p[i] = rawXmlMsg.indexOf( rawIndicators[i]);
			
			if (p[i]>=0){
				isum = isum+ 1;
			}
		}
		
		rB = (isum == p.length) ;
		
		return rB;
	}

	
	public int registrationOfInstance( long instanceID){
		int resultState = -1;
		
		int sv;
		Vector<XRegistration>  xregs ;
		XRegistration xreg ;
		
		
		xregs = messageBoard.getRegisteredInstances() ;
		
		for (int i=0;i<xregs.size();i++){
			
			xreg = xregs.get(i) ;
			if (xreg.getInstanceID() == instanceID){
				
				sv = xreg.getReceptorState() ;
				
				if ((sv >= 0) && (sv < 90)) {

					resultState = 0;
					xreg.setRegisteredSince(System.currentTimeMillis());

					break;
				}
			}
			
		} // i-> all xregs
		 
		
		
		return resultState;
	}
	
	public void updateRegistrationState( long instanceID, long transactionID, int statevalue ){ // e.g. XRegistration.RST_IS_RUNNING ) ;
		Vector<XRegistration>  xregs ;
		XRegistration xreg ;
		
		
		xregs = messageBoard.getRegisteredInstances() ;
		
		for (int i=0;i<xregs.size();i++){
			
			xreg = xregs.get(i) ;
			if (xreg.getInstanceID() == instanceID){
				
				xreg.setReceptorState(statevalue) ;
				break ;
			}
			
		} // i-> all xregs
	
	}
	
	
	public void setTransactions(Transactions transactions) {
		this.transactions = transactions;
		
	}
	
	
	
}
